#pragma once

#include <stdio.h>
#include <iostream>
#include <SFML\Graphics.hpp>

#define M_PI 3.1415f
#define LIGHT_RADIUS 100.f
#define LIGHT_FOCAL_ANGLE_RAD (M_PI*0.5f)

void Execute()
{
	sf::RenderWindow window(sf::VideoMode(800, 600), "POC perfect shadow");
	//window.setFramerateLimit(60);
	window.setVerticalSyncEnabled(true);
	window.setMouseCursorVisible(false);


	struct {
		double x;
		double y;
		double direction;
		sf::Texture texture;
		sf::Sprite sprite;
		sf::Text text;
		sf::Font font;
		sf::Vertex visibleAreaVertices[7];
	} arrow;

	arrow.x = 100;
	arrow.y = 100;
	arrow.direction = 0;
	arrow.visibleAreaVertices[0].color = sf::Color(0,0,0,100);
	for(int i = 1; i < 7; i++) { arrow.visibleAreaVertices[i].color = sf::Color(0,0,0,50); }

	arrow.texture.loadFromFile("images/arrow.png");
	arrow.texture.setSmooth(true);
	arrow.sprite.setTexture(arrow.texture);
	arrow.sprite.setOrigin(5,8);
	arrow.font.loadFromFile("data/OpenSans-Regular.ttf");
	arrow.text.setFont(arrow.font);
	arrow.text.setColor(sf::Color::Black);
	arrow.text.setCharacterSize(14);


	struct{
		sf::Texture texture;
		sf::Sprite sprite;
	} background;

	background.texture.loadFromFile("images/back.png");
	background.texture.setRepeated(true);
	background.sprite.setTexture(background.texture);
	background.sprite.setTextureRect(sf::IntRect(0,0,window.getSize().x, window.getSize().y));


	struct{
		sf::Image image;
		sf::Texture texture;
		sf::Sprite sprite;
	} obstacles;
	obstacles.image.loadFromFile("images/obstacles.png");
	obstacles.texture.loadFromImage(obstacles.image);
	obstacles.sprite.setTexture(obstacles.texture);


	struct{
		sf::RenderTexture renderTexture;
		sf::Sprite sprite;
	} scene;
	scene.renderTexture.create(window.getSize().x, window.getSize().y);
	scene.sprite.setTexture(scene.renderTexture.getTexture());


	struct{
		sf::Texture obstaclesTextureExtract;
		sf::Sprite obstaclesSprite;
		sf::RenderTexture renderTextureUnwrapped;
		sf::RectangleShape back;
		sf::Sprite spriteUnwrapped;
		sf::Image unwrappedShadowedImage;
		sf::Texture unwrappedShadowedTexture;
		sf::Sprite spriteUnwrappedShadowed;
		sf::Shader polarToRectangularShader;
	} miniView;
	miniView.back.setSize(sf::Vector2f(LIGHT_RADIUS * 2.f, LIGHT_RADIUS * 2.f));
	miniView.back.setOutlineColor(sf::Color::Black);
	miniView.back.setOutlineThickness(2);
	miniView.back.setFillColor(sf::Color::Black);
	miniView.back.setPosition(window.getSize().x - LIGHT_RADIUS * 2.f, 0.f);

	miniView.renderTextureUnwrapped.create(LIGHT_RADIUS * 2, LIGHT_RADIUS * 2);
	miniView.spriteUnwrapped.setPosition(window.getSize().x - LIGHT_RADIUS * 2, 0);
	miniView.spriteUnwrapped.setTexture(miniView.renderTextureUnwrapped.getTexture());

	miniView.obstaclesSprite.setTexture(miniView.obstaclesTextureExtract);
	miniView.obstaclesSprite.setTextureRect(sf::IntRect(0,0,LIGHT_RADIUS * 2, LIGHT_RADIUS * 2));

	miniView.spriteUnwrappedShadowed.setTexture(miniView.unwrappedShadowedTexture);
	miniView.spriteUnwrappedShadowed.setTextureRect(sf::IntRect(0,0,LIGHT_RADIUS * 2, LIGHT_RADIUS * 2));
	miniView.spriteUnwrappedShadowed.setPosition(window.getSize().x - LIGHT_RADIUS * 2, 0);

	miniView.polarToRectangularShader.loadFromFile("data/polar_to_rectangular.frag.fx", sf::Shader::Fragment);
	miniView.polarToRectangularShader.setParameter("focal", LIGHT_FOCAL_ANGLE_RAD);


    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();

			if (event.type == sf::Event::MouseWheelMoved)
			{
				arrow.direction -= event.mouseWheel.delta * 0.1;
				if(arrow.direction < 0) arrow.direction += 2 * M_PI;
				else if(arrow.direction > 2 * M_PI) arrow.direction -= 2 * M_PI;
			}
        }

		sf::Vector2i mousePosition = sf::Mouse::getPosition(window);
		arrow.x = mousePosition.x;
		arrow.y = mousePosition.y;

		arrow.sprite.setPosition(arrow.x, arrow.y);
		arrow.sprite.setRotation(arrow.direction * 180.f / M_PI);
		char angleDisplay[16];
		sprintf_s(angleDisplay, "%.2f.PI rad", arrow.direction / M_PI);
		arrow.text.setString(angleDisplay);
		arrow.text.setPosition(arrow.x, arrow.y - 50);
		arrow.visibleAreaVertices[0].position = sf::Vector2f(arrow.x, arrow.y);
		for(int i = 1; i < 7; i++)
		{
			arrow.visibleAreaVertices[i].position = 
				sf::Vector2f(
				arrow.x + cos(LIGHT_FOCAL_ANGLE_RAD / 5.f * (i - 1.f) - LIGHT_FOCAL_ANGLE_RAD * 0.5f + arrow.direction) * 100.f, 
				arrow.y + sin(LIGHT_FOCAL_ANGLE_RAD / 5.f * (i - 1.f) - LIGHT_FOCAL_ANGLE_RAD * 0.5f + arrow.direction) * 100.f);
		}

		scene.renderTexture.clear(sf::Color::White);
		scene.renderTexture.draw(background.sprite);
		scene.renderTexture.draw(obstacles.sprite);
		scene.renderTexture.display();

		// obstacle map portion extraction
		if(!miniView.obstaclesTextureExtract.loadFromImage(obstacles.image, sf::IntRect(
			(int)arrow.x - LIGHT_RADIUS,
			(int)arrow.y - LIGHT_RADIUS,
			LIGHT_RADIUS * 2,
			LIGHT_RADIUS * 2
			)))
		{
			std::cerr << "error while loading obstaclesTextureExtract" << std::endl;
		}

		// Polar to Rectangular obstacle map -> renderTextureUnwrapped
		miniView.polarToRectangularShader.setParameter("texture", miniView.obstaclesTextureExtract);
		miniView.polarToRectangularShader.setParameter("direction", arrow.direction);
		//miniView.obstaclesSprite.setTexture(miniView.obstaclesTextureExtract, true);
		miniView.renderTextureUnwrapped.clear();
		miniView.renderTextureUnwrapped.draw(miniView.obstaclesSprite, &miniView.polarToRectangularShader);
		miniView.renderTextureUnwrapped.display();

		// TODO: Rectangular shadow map -> renderTextureUnwrappedShadowed
		
		miniView.unwrappedShadowedImage = miniView.renderTextureUnwrapped.getTexture().copyToImage();
		sf::Vector2u size = miniView.unwrappedShadowedImage.getSize();
		for(int x=0; x < size.x; x++)
		{
			bool isShadow = false;
			for(int y = 0; y < size.y; y++)
			{
				if(isShadow)
				{
					miniView.unwrappedShadowedImage.setPixel(x, y, sf::Color::Black);
				}
				else if(miniView.unwrappedShadowedImage.getPixel(x, y).r == 0)
				{
					isShadow = true;
				}
			}
		}

		miniView.unwrappedShadowedTexture.loadFromImage(miniView.unwrappedShadowedImage);
		

		// TODO: Rectangular to polar shadow map -> renderTextureShadowed

		window.draw(scene.sprite);
		window.draw(miniView.back);
		window.draw(arrow.visibleAreaVertices, 7, sf::TrianglesFan);
		window.draw(arrow.sprite);
		window.draw(arrow.text);
		//window.draw(miniView.spriteUnwrapped);
		window.draw(miniView.spriteUnwrappedShadowed);

        window.display();
    }
}